/*
 * Copyright (c) zhg2yqq.com Corp.
 * All Rights Reserved.
 */
package com.zhg2yqq.wheels.mvc.wrapper;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.zhg2yqq.wheels.mvc.annotation.FieldEncrypt;
import com.zhg2yqq.wheels.mvc.util.FieldEncryptUtil;

import cn.hutool.core.text.CharSequenceUtil;

/**
 * @author zhg2yqq, 2022年12月6日
 * @version zhg2yqq v1.0
 */
public class EncryptLambdaUpdateWrapper<T> extends LambdaUpdateWrapper<T> implements BaseWapper<T> {
    private static final long serialVersionUID = 1L;
    private String encryptKey;
    private Map<String, Field> entityEncryptFields = new HashMap<>();

    public EncryptLambdaUpdateWrapper() {
        super();
        initEntityEncryptFields();
    }

    public EncryptLambdaUpdateWrapper(Class<T> entityClass) {
        super(entityClass);
        initEntityEncryptFields();
    }

    public EncryptLambdaUpdateWrapper(T entity) {
        super(entity);
        initEntityEncryptFields();
    }

    public EncryptLambdaUpdateWrapper(String key, Class<T> entityClass) {
        this(entityClass);
        this.encryptKey = key;
    }

    public EncryptLambdaUpdateWrapper(String key, T entity) {
        this(entity);
        this.encryptKey = key;
    }

    private void initEntityEncryptFields() {
        Field[] fields = getGenericTypeAllEncryptFields();
        if (fields != null) {
            for (Field f : fields) {
                String fName = CharSequenceUtil.upperFirst(f.getName());
                if (f.getType() == Boolean.class || f.getType() == boolean.class) {
                    entityEncryptFields.put("is" + CharSequenceUtil.upperFirst(fName), f);
                }
                entityEncryptFields.put("get" + CharSequenceUtil.upperFirst(fName), f);
            }
        }
    }

    protected Object getEncryptVal(boolean condition, SFunction<T, ?> column, Object val) {
        if (condition) {
            String getterName = LambdaUtils.extract(column).getImplMethodName();
            Field encryptField = entityEncryptFields.get(getterName);
            if (encryptField != null) {
                FieldEncrypt annotation = encryptField.getAnnotation(FieldEncrypt.class);
                String key = CharSequenceUtil.isEmpty(annotation.encryptorPassword()) ? encryptKey
                        : annotation.encryptorPassword();
                return FieldEncryptUtil.encrypt(key, val);
            }
        }
        return val;
    }

    protected Collection<?> getEncryptValColl(boolean condition, SFunction<T, ?> column,
                                              Collection<?> coll) {
        if (condition) {
            String getterName = LambdaUtils.extract(column).getImplMethodName();
            Field encryptField = entityEncryptFields.get(getterName);
            if (encryptField != null && coll != null) {
                FieldEncrypt annotation = encryptField.getAnnotation(FieldEncrypt.class);
                String key = CharSequenceUtil.isEmpty(annotation.encryptorPassword()) ? encryptKey
                        : annotation.encryptorPassword();
                return coll.stream().map(val -> FieldEncryptUtil.encrypt(key, val))
                        .collect(Collectors.toList());
            }
        }
        return coll;
    }

    @Override
    protected LambdaUpdateWrapper<T> addCondition(boolean condition, SFunction<T, ?> column,
                                                  SqlKeyword sqlKeyword, Object val) {
        Object value = getEncryptVal(condition, column, val);
        return super.addCondition(condition, column, sqlKeyword, value);
    }

    @Override
    public LambdaUpdateWrapper<T> in(boolean condition, SFunction<T, ?> column,
                                     Collection<?> coll) {
        Collection<?> value = getEncryptValColl(condition, column, coll);
        return super.in(condition, column, value);
    }

    @Override
    public LambdaUpdateWrapper<T> in(boolean condition, SFunction<T, ?> column, Object... values) {
        if (values == null) {
            return super.in(condition, column, values);
        }
        Collection<?> value = getEncryptValColl(condition, column, Arrays.asList(values));
        return super.in(condition, column, value.toArray());
    }

    @Override
    public LambdaUpdateWrapper<T> notIn(boolean condition, SFunction<T, ?> column,
                                        Collection<?> coll) {
        Collection<?> value = getEncryptValColl(condition, column, coll);
        return super.notIn(condition, column, value);
    }

    @Override
    public LambdaUpdateWrapper<T> notIn(boolean condition, SFunction<T, ?> column,
                                        Object... values) {
        if (values == null) {
            return super.notIn(condition, column, values);
        }
        Collection<?> value = getEncryptValColl(condition, column, Arrays.asList(values));
        return super.notIn(condition, column, value.toArray());
    }

    @Override
    public Class<T> getGenericType() {
        return getEntityClass();
    }
}
